//! Struct declaration parsers

use super::super::{parse_type, Parser};
use super::helpers::parse_where_clause_for_generics;
use super::impl_decl::parse_method_declaration;
use crate::ast::decl::*;
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;

/// Parse Go-style struct declaration (type StructName struct { ... })
pub fn parse_go_style_struct_declaration(parser: &mut Parser) -> Result<StructDecl> {
    parser.expect(&Token::Type)?;

    if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let struct_name = name.clone();
        parser.advance()?;

        let mut generics = parse_generic_params(parser)?;

        parser.expect(&Token::Struct)?;

        // Optional where-clause before '{'
        parse_where_clause_for_generics(parser, &mut generics)?;

        parse_go_style_struct(parser, struct_name, generics)
    } else {
        Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected struct name after 'type'",
        ))
    }
}

/// Parse Go-style struct declaration from current position (assumes 'type' and name are already consumed)
pub fn parse_go_style_struct_declaration_from_current(
    parser: &mut Parser,
    struct_name: String,
) -> Result<StructDecl> {
    let mut generics = parse_generic_params(parser)?;
    parser.expect(&Token::Struct)?;

    // Optional where-clause before '{'
    parse_where_clause_for_generics(parser, &mut generics)?;

    parse_go_style_struct(parser, struct_name, generics)
}

/// Parse Go-style struct declaration (struct { ... })
pub(in crate::parser) fn parse_go_style_struct(
    parser: &mut Parser,
    struct_name: String,
    generics: Vec<GenericParam>,
) -> Result<StructDecl> {
    parser.expect(&Token::LeftBrace)?;

    // Parse fields (Go-style: no comma separators)
    let mut fields = Vec::new();

    while !parser.check(&Token::RightBrace) {
        if let Some(Token::Identifier(field_name)) = parser.peek().map(|t| &t.token) {
            let name = field_name.clone();
            parser.advance()?;

            // Go-style: field name followed by type (no colon)
            let field_type = parse_type(parser)?;
            if field_type.is_none() {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected field type",
                ));
            }

            // Parse field tags (Go-style: `field_name type `tag``)
            let field_tag = if parser.consume(&Token::Backtick)? {
                if let Some(Token::String(tag)) = parser.peek().map(|t| &t.token) {
                    let tag = tag.clone();
                    parser.advance()?;
                    parser.expect(&Token::Backtick)?;
                    Some(tag)
                } else {
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected field tag string",
                    ));
                }
            } else {
                None
            };

            fields.push(crate::ast::StructField {
                name,
                field_type: field_type.unwrap(),
                default_value: None, // Go doesn't support default values in struct fields
                field_tag,
                location: parser.current_location(),
            });
        } else {
            // Skip unknown tokens
            parser.advance()?;
        }
    }

    parser.expect(&Token::RightBrace)?;

    // Don't parse methods here - let them be parsed as independent function declarations
    let methods = Vec::new();

    Ok(StructDecl {
        name: struct_name,
        generics,
        fields,
        methods,
        location: parser.current_location(),
    })
}

pub(in crate::parser) fn parse_generic_params(parser: &mut Parser) -> Result<Vec<GenericParam>> {
    if !parser.check(&Token::Less) {
        return Ok(vec![]);
    }

    parser.advance()?; // consume '<'
    let mut generics = Vec::new();

    if !parser.check(&Token::Greater) {
        loop {
            if let Some(Token::Identifier(generic_name)) = parser.peek().map(|t| &t.token) {
                let name = generic_name.clone();
                parser.advance()?;

                let constraints = if parser.consume(&Token::Colon)? {
                    let mut constraints = Vec::new();

                    loop {
                        if let Some(constraint) = parse_type(parser)? {
                            constraints.push(constraint);
                        }

                        if !parser.consume(&Token::Plus)? {
                            break;
                        }
                    }

                    constraints
                } else {
                    vec![]
                };

                generics.push(GenericParam {
                    name,
                    constraints,
                    location: parser.current_location(),
                });
            }

            if !parser.consume(&Token::Comma)? {
                break;
            }
        }
    }

    parser.expect(&Token::Greater)?;
    Ok(generics)
}

/// Parse struct declaration
pub fn parse_struct_declaration(parser: &mut Parser) -> Result<StructDecl> {
    parser.expect(&Token::Struct)?;

    if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let struct_name = name.clone();
        parser.advance()?;

        // Parse generic type parameters
        let mut generics = if parser.consume(&Token::Less)? {
            let mut generics = Vec::new();

            if !parser.check(&Token::Greater) {
                loop {
                    if let Some(Token::Identifier(generic_name)) = parser.peek().map(|t| &t.token) {
                        let name = generic_name.clone();
                        parser.advance()?;

                        // Parse constraints
                        let constraints = if parser.consume(&Token::Colon)? {
                            let mut constraints = Vec::new();

                            loop {
                                if let Some(constraint) = parse_type(parser)? {
                                    constraints.push(constraint);
                                }

                                if !parser.consume(&Token::Plus)? {
                                    break;
                                }
                            }

                            constraints
                        } else {
                            vec![]
                        };

                        generics.push(GenericParam {
                            name,
                            constraints,
                            location: parser.current_location(),
                        });
                    }

                    if !parser.consume(&Token::Comma)? {
                        break;
                    }
                }
            }

            parser.expect(&Token::Greater)?;
            generics
        } else {
            vec![]
        };

        // Optional where-clause between generics and '{'
        parse_where_clause_for_generics(parser, &mut generics)?;

        parser.expect(&Token::LeftBrace)?;

        // Parse fields (Go-style: no comma separators)
        let mut fields = Vec::new();

        while !parser.check(&Token::RightBrace) {
            if let Some(Token::Identifier(field_name)) = parser.peek().map(|t| &t.token) {
                let name = field_name.clone();
                parser.advance()?;

                // Go-style: field name followed by type (no colon)
                let field_type = parse_type(parser)?;
                if field_type.is_none() {
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected field type",
                    ));
                }

                // Parse field tags (Go-style: `field_name type `tag``)
                let field_tag = if parser.consume(&Token::Backtick)? {
                    if let Some(Token::String(tag)) = parser.peek().map(|t| &t.token) {
                        let tag = tag.clone();
                        parser.advance()?;
                        parser.expect(&Token::Backtick)?;
                        Some(tag)
                    } else {
                        return Err(CompilerError::syntax(
                            parser.current_location().line,
                            parser.current_location().column,
                            "Expected field tag string",
                        ));
                    }
                } else {
                    None
                };

                fields.push(crate::ast::StructField {
                    name,
                    field_type: field_type.unwrap(),
                    default_value: None, // Go doesn't support default values in struct fields
                    field_tag,
                    location: parser.current_location(),
                });
            } else {
                // Skip unknown tokens
                parser.advance()?;
            }
        }

        parser.expect(&Token::RightBrace)?;

        // Parse methods (only if they have receivers)
        let mut methods = Vec::new();

        // Check if the next token is 'fn' and if it's followed by a receiver
        while parser.check(&Token::Fn) {
            // Peek ahead to see if this is a method (has receiver) or a regular function
            let current_pos = parser.current.clone();
            parser.advance()?; // consume 'fn'

            if parser.check(&Token::LeftParen) {
                // This looks like a method with receiver, try to parse it
                if let Some(method) = parse_method_declaration(parser)? {
                    methods.push(method);
                } else {
                    // If parsing failed, it might be a regular function, restore position and break
                    parser.current = current_pos;
                    break;
                }
            } else {
                // This is a regular function, restore position and break
                parser.current = current_pos;
                break;
            }
        }

        Ok(StructDecl {
            name: struct_name,
            generics,
            fields,
            methods,
            location: parser.current_location(),
        })
    } else {
        Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected struct name after 'struct'",
        ))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::Parser;

    fn parse_struct_decl(input: &str) -> Result<StructDecl> {
        let mut parser = Parser::new(input.to_string(), None);
        parser.advance()?;
        parse_go_style_struct_declaration(&mut parser)
    }

    #[test]
    fn test_parse_simple_struct() {
        let result = parse_struct_decl("type Person struct { name string age int }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "Person");
        assert_eq!(decl.fields.len(), 2);
        assert_eq!(decl.fields[0].name, "name");
        assert_eq!(decl.fields[1].name, "age");
    }

    #[test]
    fn test_parse_struct_with_no_fields() {
        let result = parse_struct_decl("type Empty struct { }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "Empty");
        assert_eq!(decl.fields.len(), 0);
    }

    #[test]
    fn test_parse_struct_with_generics() {
        let result = parse_struct_decl("type Box<T> struct { value T }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.generics.len(), 1);
        assert_eq!(decl.generics[0].name, "T");
    }

    #[test]
    fn test_parse_struct_no_name_error() {
        let result = parse_struct_decl("type struct { x int }");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_struct_multiple_fields() {
        let result = parse_struct_decl("type Point struct { x float y float z float }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.fields.len(), 3);
        assert_eq!(decl.fields[0].name, "x");
        assert_eq!(decl.fields[1].name, "y");
        assert_eq!(decl.fields[2].name, "z");
    }

    #[test]
    fn test_parse_struct_with_complex_types() {
        // Use fixed-size array [10]int instead of []int
        let result = parse_struct_decl("type Data struct { items [10]int count int }");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.fields.len(), 2);
    }
}
